﻿namespace FiftyOne.Foundation.Mobile.Detection
{
    using FiftyOne;
    using FiftyOne.Foundation.Mobile;
    using FiftyOne.Foundation.Mobile.Configuration;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Threading;
    using System.Web;

    internal class RequestHistory
    {
        private static DateTime _lastAccessedTime = DateTime.MinValue;
        private static DateTime _nextServiceTime = DateTime.MinValue;
        private static string _syncFilePath = null;
        private const int FILE_SERVICE_FREQUENCY = 1;
        private const int RECORD_LENGTH = 0x10;
        private const int TIMEOUT = 0x2710;

        static RequestHistory()
        {
            if (Manager.Redirect.Enabled)
            {
                _syncFilePath = Support.GetFilePath(Manager.Redirect.DevicesFile);
                if (_syncFilePath == string.Empty)
                {
                    _syncFilePath = null;
                }
                if (_syncFilePath != null)
                {
                    ProcessSyncFile();
                }
            }
        }

        private static void AddToCache(object record)
        {
            if (record is RequestRecord)
            {
                using (FileStream stream = OpenSyncFilePath(FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                {
                    if (stream != null)
                    {
                        ((RequestRecord) record).Write(stream);
                        stream.Flush();
                        stream.Close();
                    }
                }
            }
        }

        private static void CheckIfServiceRequired()
        {
            if (_nextServiceTime < DateTime.UtcNow)
            {
                long state = ((PreviousDevices.lastDevice.LastActiveDate == DateTime.MinValue.Ticks) ? DateTime.UtcNow.Ticks : PreviousDevices.lastDevice.LastActiveDate) - (0x23c34600L * Manager.Redirect.Timeout);
                ThreadPool.QueueUserWorkItem(new WaitCallback(RequestHistory.ServiceRequestHistory), state);
            }
        }

        internal static bool IsPresent(HttpRequest request)
        {
            if (_syncFilePath != null)
            {
                RequestRecord record = new RequestRecord(request);
                RefreshSyncFile();
                bool flag = PreviousDevices.Devices.ContainsKey(record.Key);
                AddToCache(record);
                CheckIfServiceRequired();
                return flag;
            }
            return false;
        }

        private static FileStream OpenSyncFilePath(FileMode mode, FileAccess access, FileShare share)
        {
            FileStream stream = null;
            if (_syncFilePath != null)
            {
                DateTime time = DateTime.UtcNow.AddMilliseconds(10000.0);
                try
                {
                    stream = System.IO.File.Open(_syncFilePath, mode, access, share);
                }
                catch (IOException exception)
                {
                    EventLog.Info(exception);
                    stream = null;
                    Thread.Sleep(new Random().Next(200));
                }
                while ((stream == null) && (DateTime.UtcNow < time))
                {
                }
                if (stream == null)
                {
                    throw new MobileException(string.Format("Could not open request history file '{0}' in mode '{1}', with access '{2}' and share '{3}'.", new object[] { _syncFilePath, mode, access, share }));
                }
            }
            return stream;
        }

        private static void ProcessSyncFile()
        {
            if (System.IO.File.Exists(_syncFilePath))
            {
                lock (PreviousDevices.Devices)
                {
                    using (FileStream stream = OpenSyncFilePath(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        if (stream != null)
                        {
                            long length = stream.Length;
                            BinaryReader reader = new BinaryReader(stream);
                            RequestRecord recordToCopy = new RequestRecord();
                            RequestRecord record2 = null;
                            long num2 = length / 0x10L;
                            for (long i = 0L; i < num2; i += 1L)
                            {
                                try
                                {
                                    stream.Position = ((num2 - i) * 0x10L) - 0x10L;
                                    recordToCopy.Read(reader);
                                }
                                catch (EndOfStreamException exception)
                                {
                                    EventLog.Debug(exception);
                                    break;
                                }
                                if (recordToCopy.CompareTo(PreviousDevices.lastDevice) == 0)
                                {
                                    break;
                                }
                                if (PreviousDevices.Devices.ContainsKey(recordToCopy.Key))
                                {
                                    PreviousDevices.Devices[recordToCopy.Key] = recordToCopy.LastActiveDate;
                                }
                                else
                                {
                                    PreviousDevices.Devices.Add(recordToCopy.Key, recordToCopy.LastActiveDate);
                                }
                                if (record2 == null)
                                {
                                    record2 = new RequestRecord(recordToCopy);
                                }
                            }
                            if ((length == stream.Length) && (record2 != null))
                            {
                                PreviousDevices.lastDevice = record2;
                            }
                            reader.Close();
                            stream.Close();
                        }
                    }
                }
            }
        }

        private static byte[] ReadRecords(FileStream stream, long purgeDate)
        {
            byte[] buffer = null;
            long num = 0L;
            BinaryReader reader = new BinaryReader(stream);
            RequestRecord record = new RequestRecord();
            stream.Position = 0L;
            num = 0L;
            while (num < stream.Length)
            {
                record.Read(reader);
                if (record.LastActiveDate > purgeDate)
                {
                    break;
                }
                num += 0x10L;
            }
            if ((num > 0L) && (num < stream.Length))
            {
                int count = (int) (stream.Length - num);
                buffer = new byte[count];
                stream.Position = num;
                stream.Read(buffer, 0, count);
            }
            return buffer;
        }

        private static void RefreshSyncFile()
        {
            FileInfo info = new FileInfo(_syncFilePath);
            if ((info != null) && (info.LastAccessTimeUtc > _lastAccessedTime))
            {
                ProcessSyncFile();
                _lastAccessedTime = info.LastAccessTimeUtc;
            }
        }

        private static void ServiceRequestHistory(object purgeDate)
        {
            using (FileStream stream = OpenSyncFilePath(FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
            {
                if (stream != null)
                {
                    long length = stream.Length;
                    byte[] buffer = ReadRecords(stream, (long) purgeDate);
                    if ((buffer != null) && (stream.Length == length))
                    {
                        stream.Position = 0L;
                        stream.Write(buffer, 0, buffer.Length);
                        stream.Flush();
                        stream.SetLength((long) buffer.Length);
                        stream.Flush();
                        EventLog.Info(string.Format("Trimmed request history file '{0}' by removing {1} bytes.", _syncFilePath, length - buffer.Length));
                    }
                    stream.Close();
                }
            }
            lock (PreviousDevices.Devices)
            {
                int index = 0;
                while (index < PreviousDevices.Devices.Count)
                {
                    if (PreviousDevices.Devices.Values[index] < ((long) purgeDate))
                    {
                        PreviousDevices.Devices.RemoveAt(index);
                    }
                    else
                    {
                        index++;
                    }
                }
            }
            _nextServiceTime = DateTime.UtcNow.AddMinutes(1.0).AddSeconds((double) new Random().Next(30));
            GC.Collect();
        }

        internal protected class PreviousDevices
        {
            protected internal static SortedList<long, long> Devices = new SortedList<long, long>();
            protected internal static RequestHistory.RequestRecord lastDevice = new RequestHistory.RequestRecord();
        }

        internal protected class RequestRecord : IComparable
        {
            private long _key;
            private long _lastActiveDate;
            private static readonly string[] ADDITIONAL_HEADERS = new string[] { 
                "Accept-Language", "Host", "Via", "UA", "x-forwarded-for", "x-source-id", "x-wap-profile", "x-forwarded-host", "x-forwarded-server", "x-up-calling-line-id", "x-nokia-alias", "x-nokia-msisdn", "x-nokia-ipaddress", "x-nokia-imsi", "x-imsi", "x-msisdn", 
                "x-avantgo-userid"
             };

            protected internal RequestRecord()
            {
                this._key = 0L;
            }

            protected internal RequestRecord(RequestHistory.RequestRecord recordToCopy)
            {
                this._key = 0L;
                this._key = recordToCopy.Key;
                this._lastActiveDate = recordToCopy.LastActiveDate;
            }

            protected internal RequestRecord(BinaryReader reader)
            {
                this._key = 0L;
                this._key = reader.ReadInt64();
                this._lastActiveDate = reader.ReadInt64();
            }

            protected internal RequestRecord(HttpRequest request)
            {
                int num;
                this._key = 0L;
                byte[] buffer = new byte[8];
                byte[] addressBytes = IPAddress.Parse(request.UserHostAddress).GetAddressBytes();
                if (addressBytes.Length == 4)
                {
                    for (num = 0; num < 4; num++)
                    {
                        buffer[7 - num] = addressBytes[num];
                    }
                    SetHashCode(buffer, request);
                    this._key = BitConverter.ToInt64(buffer, 0);
                }
                else if (addressBytes.Length == 8)
                {
                    this._key = BitConverter.ToInt64(addressBytes, 0);
                }
                else
                {
                    byte[] bytes = BitConverter.GetBytes(addressBytes.GetHashCode());
                    for (num = 0; num < 4; num++)
                    {
                        buffer[4 + num] = bytes[num];
                    }
                    SetHashCode(buffer, request);
                    this._key = BitConverter.ToInt64(buffer, 0);
                }
                this._lastActiveDate = DateTime.UtcNow.Ticks;
            }

            public int CompareTo(object obj)
            {
                if (!(obj is RequestHistory.RequestRecord))
                {
                    throw new MobileException(string.Format("Can not compare object of type '{0}' with '{1}'.", obj.GetType().ToString(), base.GetType().ToString()));
                }
                RequestHistory.RequestRecord record = (RequestHistory.RequestRecord) obj;
                if (record.LastActiveDate < this.LastActiveDate)
                {
                    return -1;
                }
                if (record.LastActiveDate > this.LastActiveDate)
                {
                    return 1;
                }
                if (record.Key < this.Key)
                {
                    return -1;
                }
                if (record.Key > this.Key)
                {
                    return 1;
                }
                return 0;
            }

            protected internal void Read(BinaryReader reader)
            {
                this._key = reader.ReadInt64();
                this._lastActiveDate = reader.ReadInt64();
            }

            private static void SetHashCode(byte[] buffer, HttpRequest request)
            {
                StringBuilder builder = new StringBuilder();
                builder.Append(request.UserAgent);
                foreach (string str in ADDITIONAL_HEADERS)
                {
                    if (request.Headers[str] != null)
                    {
                        builder.Append(str).Append(request.Headers[str]);
                    }
                }
                int hashCode = builder.ToString().GetHashCode();
                buffer[0] = (byte) hashCode;
                buffer[1] = (byte) (hashCode >> 8);
                buffer[2] = (byte) (hashCode >> 0x10);
                buffer[3] = (byte) (hashCode >> 0x18);
            }

            protected internal void Write(Stream stream)
            {
                BinaryWriter writer = new BinaryWriter(stream);
                writer.Write(this._key);
                writer.Write(this._lastActiveDate);
            }

            protected internal long Key
            {
                get
                {
                    return this._key;
                }
            }

            protected internal long LastActiveDate
            {
                get
                {
                    return this._lastActiveDate;
                }
                set
                {
                    this._lastActiveDate = value;
                }
            }
        }
    }
}

